package com.jay.compass.bt;

import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.Formatter;
import java.util.Set;

import com.jay.compass.MainActivity;
import com.jay.compass.R;
import com.jay.compass.app.StarApplication;
import com.jay.compass.utils.BTimer;

import android.annotation.SuppressLint;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.widget.ArrayAdapter;

@SuppressLint("NewApi")
public class BluetoothService extends Service {

	public final static String ACTION_BLUETOOTH_CONNECTED = "com.jay.compass.ACTION_BLUETOOTH_CONNECTED";
	public final static String ACTION_BLUETOOTH_DISCONNECTED = "com.jay.compass.ACTION_BLUETOOTH_DISCONNECTED";
	public final static String ACTION_BLUETOOTH_TIMEOUT = "com.jay.compass.ACTION_BLUETOOTH_TIMEOUT";
	public final static String ACTION_BLUETOOTH_TOAST = "com.jay.compass.ACTION_BLUETOOTH_TOAST";
	public final static String ACTION_BLUETOOTH_NEW_MESSAGE = "com.jay.compass.ACTION_BLUETOOTH_NEW_MESSAGE";
	public final static String ACTION_BLUETOOTH_SEND_SUCCESS = "com.jay.compass.ACTION_BLUETOOTH_SEND_SUCCESS";
	public final static String ACTION_BLUETOOTH_SEND_FAILED = "com.jay.compass.ACTION_BLUETOOTH_SEND_FAILED";
	public final static String ACTION_BLUETOOTH_LOCATE_SUCCESS = "com.jay.compass.ACTION_BLUETOOTH_LOCATE_SUCCESS";
	public final static String ACTION_BLUETOOTH_LOCATE_FAILED = "com.jay.compass.ACTION_BLUETOOTH_LOCATE_FAILED";
	public final static String ACTION_BLUETOOTH_LOCATE = "com.jay.compass.ACTION_BLUETOOTH_LOCATE";

	public static final int MESSAGE_STATE_CHANGE = 1;
	public static final int MESSAGE_READ = 2;
	public static final int MESSAGE_WRITE = 3;
	public static final int MESSAGE_DEVICE_NAME = 4;
	public static final int MESSAGE_TOAST = 5;
	public static final String DEVICE_NAME = "device_name";

	public String encoding = "GB18030";

	public int[] buffer = new int[1000];
	public int pbuffer = 0;
	public boolean isCompleted = false;
	public boolean isFirstPacket = true;
	public int thisPackageLen = 0;

	protected Btcs mBtService = null;
	protected ArrayAdapter<String> mArrayPaired;
	Set<BluetoothDevice> pairedDevices;
	byte[] p_ref;
	private BluetoothAdapter mBluetoothAdapter;

	private int SERVICE_INTERVAL;
	public int MY_UID;
	private int signal[] = new int[6];
	private int battery;

	public static final String BROADCAST_TO_TOAST = "TO BOAST";
	public static final String NEW_MESSAGE_ID = "NEW_MESSAGE_ID";
	public static final String NEW_MESSAGE_MESSAGE = "NEW_MESSAGE_MESSAGE";
	public static final String LOCATE_LAT = "LOCATE_LAT";
	public static final String LOCATE_LNG = "LOCATE_LNG";

	public static boolean isLocateOp = false;

	private ServiceBinder serviceBinder = new ServiceBinder();

	private void broadcastUpdate(final String action) {
		final Intent intent = new Intent(action);
		sendBroadcast(intent);
	}

	private void broadcastUpdate(final String action, String message) {
		final Intent intent = new Intent(action);
		intent.putExtra(BROADCAST_TO_TOAST, message);
		sendBroadcast(intent);
	}

	private void handlerNewMessage(String id, String message) {
		final Intent intent = new Intent(ACTION_BLUETOOTH_NEW_MESSAGE);
		intent.putExtra(NEW_MESSAGE_ID, id);
		intent.putExtra(NEW_MESSAGE_MESSAGE, message);

		sendBroadcast(intent);
	}

	private void handlerLocate(float lat, float lng) {
		final Intent intent = new Intent(ACTION_BLUETOOTH_LOCATE);
		intent.putExtra(LOCATE_LAT, lat);
		intent.putExtra(LOCATE_LNG, lng);

		sendBroadcast(intent);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return serviceBinder;
	}

	public class ServiceBinder extends Binder implements IBluetoothService {

		@Override
		public void connect() {
			// TODO Auto-generated method stub
			pairedDevices = mBluetoothAdapter.getBondedDevices();
			if (mBtService == null)
				setupBT();
			mBtService.scan(true);
			for (int i = 0; i < 10; i++) {
				try {
					Thread.sleep(500);
					if (connectBt2_0()) {
						return;
					}
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			broadcastUpdate(ACTION_BLUETOOTH_TIMEOUT);
		}

		@Override
		public void disconnect() {
			// TODO Auto-generated method stub
			stopBT();
		}

		@Override
		public void sendCommand(int type, String recvId, String content) {
			// TODO Auto-generated method stub
			if (type == MainActivity.SEND_TYPE_ICJC) {
				icjc();
			} else if (type == MainActivity.SEND_TYPE_XTZJ) {
				xtzj();
			} else if (type == MainActivity.SEND_TYPE_DWSQ) {
				dwsq();
			} else if (type == MainActivity.SEND_TYPE_TXSQ) {
				sendMsg(recvId, content);
			}
		}

		@Override
		public void startBt() {
			// TODO Auto-generated method stub
		}

		@Override
		public void stopBt() {
			// TODO Auto-generated method stub
			// stopBT();
		}

		@Override
		public int getUid() {
			// TODO Auto-generated method stub
			return MY_UID;
		}

		@Override
		public void setIsLocate(boolean is) {
			// TODO Auto-generated method stub
			isLocateOp = is;
		}
	}

	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
		final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
		mBluetoothAdapter = bluetoothManager.getAdapter();
		mBluetoothAdapter.getBondedDevices();
	}

	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.onDestroy();
	}

	private boolean connectBt2_0() {
		int btCount = mArrayPaired.getCount();
		for (int i = 0; i < btCount; i++) {
			System.out.println("paired device: " + mArrayPaired.getItem(i));
			if (mArrayPaired.getItem(i).startsWith("CPLB")
			/* && (mArrayPaired.getItem(i).contains("dBm")) */) {
				BluetoothDevice device = mBluetoothAdapter
						.getRemoteDevice(mArrayPaired.getItem(i).toString()
								.split("\n")[1]);
				System.out.println("name = " + device.getName());
				mBtService.connect(device, true);
				return true;
			}
		}
		return false;
	}

	private void stopBT() {
		// unregisterReceiver(mBtReceiver);
		if (mBtService != null) {
			mBtService.stop(true);
			mBtService = null;
		}
	}

	/**
	 * Sets up Arrays and filter, starts mReceiver and mBtService
	 */
	private void setupBT() {
		mArrayPaired = new ArrayAdapter<String>(this, R.layout.bt_list,
				R.id.list);
		mArrayPaired.clear();
		for (BluetoothDevice device : pairedDevices) {
			mArrayPaired.add(device.getName() + "\n" + device.getAddress());
		}
		IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
		filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
		filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
		filter.addAction(BluetoothDevice.ACTION_UUID);
		filter.addAction(BluetoothDevice.EXTRA_UUID);
		filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
		filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
		registerReceiver(mBtReceiver, filter);
		mBtService = new Btcs(mBtHandler);
		p_ref = new byte[mArrayPaired.getCount()];
		Arrays.fill(p_ref, (byte) 0);
	}

	/**
	 * Catches event from Bluetooth
	 */
	private final BroadcastReceiver mBtReceiver = new BroadcastReceiver() {
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			// When discovery finds a device
			if (BluetoothDevice.ACTION_FOUND.equals(action)) {
				// Get the BluetoothDevice object from the Intent
				short rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI,
						Short.MIN_VALUE);
				BluetoothDevice device = intent
						.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
				try {
					int size = mArrayPaired.getCount();
					for (int i = 0; i < size; i++) {
						if (mArrayPaired.getItem(i).toString()
								.contains(device.getName())) {
							mArrayPaired.remove(mArrayPaired.getItem(i));
							mArrayPaired.insert((device.getName() + " (" + rssi
									+ "dBm)" + "\n" + device.getAddress()), i);
							p_ref[i] = 1;
						}
					}
				} catch (Exception e) {
				}
			} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
				pairedDevices = null;
				pairedDevices = mBluetoothAdapter.getBondedDevices();
				mArrayPaired.clear();
				for (BluetoothDevice device2 : pairedDevices) {
					mArrayPaired.add(device2.getName() + "\n"
							+ device2.getAddress());
					byte[] temp = p_ref;
					int size = temp.length;
					p_ref = new byte[size + 1];
					Arrays.fill(p_ref, (byte) 0);
					for (int i = 0; i < size; i++) {
						p_ref[i] = temp[i];
					}
				}
			} else if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
				mBtService.setState(-1);
				p_ref = new byte[mArrayPaired.getCount()];
				Arrays.fill(p_ref, (byte) 0);
			} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED
					.equals(action)) {
				mBtService.setState(-1);
				int size = p_ref.length;
				for (int i = 0; i < size; i++) {
					if (p_ref[i] != 1) {
						try {
							if (mArrayPaired.getItem(i).toString()
									.contains("dBm")) {
								String temp = mArrayPaired.getItem(i)
										.toString();
								temp = temp.split("\n")[0].split(" \\(\\-")[0]
										+ "\n" + temp.split("\n")[1];
								mArrayPaired.remove(mArrayPaired.getItem(i));
								mArrayPaired.insert(temp, i);
							}
						} catch (Exception e) {
						}
					}
				}
			} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
				mBtService.stop(false);
				mBtService.setState(-1);
			} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
				if (mBluetoothAdapter.isEnabled()) { // Just turned on
					mBtService = null;
					setupBT();
				} else { // Just turned off
					mBtService = null;
				}
			}
		}
	};

	/**
	 * Receives and processes messages from the mBtService
	 */
	@SuppressLint("HandlerLeak")
	private final Handler mBtHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MESSAGE_STATE_CHANGE:
				try {
					switch (msg.arg1) {
					case Btcs.STATE_SCANNING:
						System.out.println("STATE_SCANNING");
						break;
					case Btcs.STATE_CONNECTED:
						System.out.println("STATE_CONNECTED");
						broadcastUpdate(ACTION_BLUETOOTH_CONNECTED);
						// mBtConnected = true;
						break;
					case Btcs.STATE_CONNECTING:
						System.out.println("STATE_CONNECTING");
						// mBtConnected = false;
						break;
					case Btcs.STATE_LISTEN:
						break;
					case Btcs.STATE_NONE:
						broadcastUpdate(ACTION_BLUETOOTH_DISCONNECTED);

						break;
					}
				} catch (Exception e) {
				}
				break;
			case MESSAGE_READ:
				int bytes = (Integer) msg.obj;
				if (isDataValid(bytes)) {
					parseCmd();
					cleanBuffer();
				}
				break;
			case MESSAGE_DEVICE_NAME:
				break;
			}
		}
	};

	private void cleanBuffer() {
		Arrays.fill(buffer, (byte) 0);
		pbuffer = 0;
		thisPackageLen = 0;
	}

	private boolean isDataValid(int d) {
		buffer[pbuffer++] = d;
		if (pbuffer == 1 && buffer[0] != 36) {
			cleanBuffer();
			return false;
		}
		/*
		 * make sure we don't overrun buffer
		 */
		if ((pbuffer < (buffer[6] + buffer[5] * 256)) || pbuffer < 7) {
			Log.i("BT", "WAIT, NOT ENOUGH DATA");
			return false;
		} else if (pbuffer > (buffer[6] + buffer[5] * 256)) {
			cleanBuffer();
			Log.i("BT", "TOO MUCH DATA");
			return false;
		} else {
			thisPackageLen = buffer[6] + buffer[5] * 256;
			Log.i("BT", "DATA OK!");
			return true;
		}
	}

	/**
	 * Converts Int Array to Byte Array
	 * 
	 * @param iarr
	 * @return
	 */
	public byte[] iarr2barr(int[] iarr) {
		int len = iarr.length;
		byte[] barr = new byte[len];
		for (int i = 0; i < len; i++) {
			if (iarr[i] < 128) {
				barr[i] = (byte) iarr[i];
			} else {
				barr[i] = (byte) (iarr[i] - 256);
			}
		}
		return barr;
	}

	/**
	 * Converts Byte Array to Int Array
	 * 
	 * @param barr
	 * @return
	 */
	public static int[] barr2iarr(byte[] barr) {
		int len = barr.length;
		int[] iarr = new int[len];
		for (int i = 0; i < len; i++) {
			if (barr[i] > -1) {
				iarr[i] = barr[i];
			} else {
				iarr[i] = barr[i] + 256;
			}
		}
		return iarr;
	}

	/**
	 * converts an Int to 3 Byte Array
	 * 
	 * @param value
	 * @return
	 */
	public static final byte[] int2barr(int value) {
		return new byte[] { (byte) (value >>> 16), (byte) (value >>> 8),
				(byte) value };
	}

	/**
	 * Convert byte[] to Hex String
	 * 
	 * @param bytes
	 * @return
	 */
	public static String bytesToHexString(byte[] bytes) {
		StringBuilder sb = new StringBuilder(bytes.length * 2);
		Formatter formatter = new Formatter(sb);
		for (byte b : bytes) {
			formatter.format("%02x", b);
			sb.append(" ");
		}
		formatter.close();
		return sb.toString();
	}

	public void parseCmd() {
		try {
			int[] imsg = new int[thisPackageLen];
			for (int i = 0; i < thisPackageLen; i++)
				imsg[i] = buffer[i];
			byte[] barr = iarr2barr(imsg);
			int check = 0;
			int len = imsg.length - 1;
			for (int i = 0; i < len; i++) {
				check = (imsg[i] ^ check);
			}

			if (check != imsg[len]) {
			}

			String msg = new String(barr);
			if (msg.startsWith("$ICXX")) {
				if ((imsg[16] + imsg[15] * 256) != 0) { // FIXME Don't update
														// time
														// if 0
					SERVICE_INTERVAL = imsg[16] + imsg[15] * 256;
				}

				int ID = imsg[9] + imsg[8] * 256 + imsg[7] * 65536;
				broadcastUpdate(ACTION_BLUETOOTH_TOAST, "ID = " + ID);
				MY_UID = ID;
				StarApplication.getInstance().getSpUtil().setMyId(MY_UID);
			} else if (msg.startsWith("$ZJXX")) {
				int N[] = { imsg[14], imsg[15], imsg[16], imsg[17], imsg[18],
						imsg[19] };
				String tempString = "signal: ";
				len = N.length;
				for (int i = 0; i < signal.length; i++) {
					signal[i] = N[i];
					tempString += "" + signal[i] + " ";
				}
				battery = imsg[12];

				broadcastUpdate(ACTION_BLUETOOTH_TOAST, tempString);

			} else if (msg.startsWith("$FKXX")) {

				if (imsg[10] == 0) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "成功");
					if (isLocateOp) {
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_SUCCESS);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_SUCCESS);
					}
				} else if (imsg[10] == 1) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "失败");
					if (isLocateOp) {
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_FAILED);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
					}
				} else if (imsg[10] == 2) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "信号未锁定");
					if (isLocateOp) {
						System.out.println("unlock");
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_FAILED);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
					}
				} else if (imsg[10] == 3) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "电量低");
				} else if (imsg[10] == 4) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "发射频度未到");
					if (isLocateOp) {
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_FAILED);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
					}
				} else if (imsg[10] == 5) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "加解密错误");
					if (isLocateOp) {
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_FAILED);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
					}
				} else if (imsg[10] == 6) {
					broadcastUpdate(ACTION_BLUETOOTH_TOAST, "CRC校验错误");
					if (isLocateOp) {
						broadcastUpdate(ACTION_BLUETOOTH_LOCATE_FAILED);
					} else {
						broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
					}
				}

				if (isLocateOp) {
					isLocateOp = false;
				}

			} else if (msg.startsWith("$DWXX")) {
				float Lat = imsg[22] + ((float) imsg[23]) / 60.0f
						+ ((float) imsg[24]) / 3600.0f;
				float Lng = imsg[18] + ((float) imsg[19]) / 60.0f
						+ ((float) imsg[20]) / 3600.0f;
				handlerLocate(Lat, Lng);
			} else if (msg.startsWith("$TXXX")) {
				int ID = imsg[13] + imsg[12] * 256 + imsg[11] * 65536;
				int mlen = (imsg[17] + imsg[16] * 256) / 8;
				byte[] themsg = new byte[mlen - 1];
				for (int i = 0; i < mlen - 1; i++) {
					themsg[i] = barr[i + 19];
				}
				String actual_msg = new String(themsg);
				if (actual_msg.contains("�") == true) {
					actual_msg = new String(themsg, encoding);
				}
				broadcastUpdate(ACTION_BLUETOOTH_TOAST, "ID: " + ID
						+ "  message: " + actual_msg);
				handlerNewMessage("" + ID, actual_msg);

			}
		} catch (Exception e) {
		}
	}

	private void icjc() {
		byte[] send = { 36, 73, 67, 74, 67, 0, 12, 0, 0, 0, 0, 43 };
		mBtService.write(send);
	}

	private void xtzj() {
		byte[] send = { 36, 88, 84, 90, 74, 0, 13, 0, 0, 0, 0, 0, 53 };
		mBtService.write(send);
	}

	private void dwsq() {
		// byte[] send1 = { 36, 68, 87, 83, 81, 0, 22, 0, 0, 0, 4, 0, 0, 0, 0,
		// 0,
		// 0, 0, 0, 0 };
		// byte[] send2 = { 0, 39 };

		final byte[] send3 = { 36, 68, 87, 83, 81, 0, 22, 0, 0, 0, 4, 0, 0, 0,
				0, 0, 0, 0, 0, 0, 0, 39 };

		BTimer timer = BTimer.getInstance();
		if (timer == null) {
			mBtService.write(send3);
			timer = new BTimer(SERVICE_INTERVAL);
			BTimer.setTimer(timer);
			timer.start();
			isLocateOp = true;
		} else {
			int time_L = timer.gettimeleft();
			broadcastUpdate(ACTION_BLUETOOTH_TOAST, "等待时间：" + time_L + "s");
		}
	}

	public void sendMsg(String id, String content) {
		String contString = content;
		if (contString.length() > 0) {
			try {
				int uid = Integer.valueOf(id);

				byte[] to_uid_b = int2barr(uid);
				byte[] bmsg;
				bmsg = content.getBytes(encoding);
				Log.i("SEND-PRE MESSAGE (String)", new String(bmsg));
				Log.i("Pre-msg (Hex-String)", bytesToHexString(bmsg));

				byte[] bmsg_l = int2barr(bmsg.length * 8 + 8);
				byte[] MU = int2barr(MY_UID);
				byte[] pre_uid = { 36, 84, 88, 83, 81, 0, 0, MU[0], MU[1],
						MU[2], 70 };
				byte[] post_uid = { bmsg_l[1], bmsg_l[2], 0, -92 };
				int prlen = pre_uid.length;
				int tolen = to_uid_b.length;
				int polen = post_uid.length;
				int mslen = bmsg.length;
				int len = prlen + tolen + polen + mslen;
				int total_len = len + 1;
				byte[] combined = new byte[total_len];
				for (int i = 0; i < len; i++) {
					if (i < prlen) {
						combined[i] = pre_uid[i];
					} else if (i < prlen + tolen) {
						combined[i] = to_uid_b[i - prlen];
					} else if (i < prlen + tolen + polen) {
						combined[i] = post_uid[i - prlen - tolen];
					} else {
						combined[i] = bmsg[i - prlen - tolen - polen];
					}
				}
				byte[] ml = int2barr(total_len);
				combined[5] = ml[1];
				combined[6] = ml[2];
				int[] icombined = barr2iarr(combined);
				for (int i = 0; i < len; i++) {
					if (i > 0) {
						icombined[len] = icombined[i] ^ icombined[len];
					} else {
						icombined[len] = icombined[i];
					}
				}
				txsq(iarr2barr(icombined));
			} catch (UnsupportedEncodingException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private void txsq(final byte[] send) {
		BTimer mTimer = BTimer.getInstance();
		if (mTimer == null) {
			mBtService.write(send);
			mTimer = new BTimer(SERVICE_INTERVAL);
			BTimer.setTimer(mTimer);
			mTimer.start();
		} else {
			int time_L = mTimer.gettimeleft();
			broadcastUpdate(ACTION_BLUETOOTH_SEND_FAILED);
			broadcastUpdate(ACTION_BLUETOOTH_TOAST, "等待时间：" + time_L + "s");
		}
	}
}
